<?php
namespace Helpers;

use Admin\Models\overt\XhovertValidcode;
use CFPropertyList\CFPropertyList;
use Phalcon\Di;
use OSS\OssClient;
use OSS\Core\OssException;
use Phalcon\Mvc\Dispatcher;
use Phalcon\Db\Adapter\Pdo\Mysql;
use Phalcon\Paginator\Adapter\QueryBuilder;
use Admin\Models\school\XhschoolInfo;
use Admin\Models\user\XhsysUser;
use Admin\Models\log\XhlogCommit;
use Admin\Models\log\XhlogError;
use Admin\Models\sys\XhsysConfig;
use Admin\Models\sys\XhsysTree;
use Admin\Models\sys\XhsysEnum;
use Admin\Models\sys\XhsysUploadfile;

class Main
{

    /**
     * 获取数据库连接
     *
     * @return \Phalcon\Db\Adapter\Pdo\Mysql
     *
     * create by ck 20180913
     */
    public static function getDB()
    {
        return Di::getDefault()->get('db');
    }

    /**
     * 跳转到登陆页并添加回调url
     *
     * create by ck 20180222
     */
    public static function goLoginPage()
    {
        $baseUrl = preg_replace('/(public)*([\/\\\\])index.php$/', '', $_SERVER['PHP_SELF']) ?: '/';
        $backUrl = $_SERVER['REQUEST_URI'];
        $backUrl = substr_replace($backUrl, '/', strpos($backUrl, $baseUrl), strlen($baseUrl));
        if (strpos($backUrl, '/mobile/') === false) {
            $url = $baseUrl . 'index/login?backUrl=' . urlencode($backUrl);
        } else {
            $url = $baseUrl . 'mobile/index/login?backUrl=' . urlencode($backUrl);
        }
        header('location:' . $url);
    }

    /**
     * 获取开发密钥
     *
     * @param bool $new
     * @return string
     *
     * create by ck 20180529
     * modify by ck 20181011
     */
    public static function getSecretKey($new = false)
    {
        $time  = time();
        $year  = date('Y', $time);
        $month = date('n', $time);
        $day   = date('j', $time);
        $hour  = date('G', $time);
        $key = sprintf(
            $new
                ? 'xuehai%d&zhitongyun%d$100fen%d@%d'
                : '*#xuehai%d&zhitongyun%d$100fen%d@%d#*',
            $year,
            $month,
            $day,
            $hour
        );
        $key = md5(base64_encode($key) . "\n");
        $start = $new
            ? ($hour >= 12 ? 16 : 0)
            : ($hour >= 12 ? 0 : 16);
        $key = substr($key, $start, 16);
        return $key;
//        return ['time' => $hour, 'key' => $key];

//        $time  = time();
//        $keys  = [];
//        $year  = date('Y', $time);
//        $month = date('n', $time);
//        $day   = date('j', $time);
//        $hour  = date('G', $time);
//        for ($i = 0; $i < $num; $i++) {
//            if ($hour >= 24) break;
//            $key = sprintf('*#xuehai%d&zhitongyun%d$100fen%d@%d#*', $year, $month, $day, $hour);
//            $key = md5(base64_encode($key) . "\n");
//            $key = substr($key, $hour >= 12 ? 0 : 16, 16);
//            $keys[] = ['time' => $hour, 'key' => $key];
//            $hour++;
//        }
//        return $keys;
    }

    /**
     * 获取session中用户信息数组，有字段则返回字段值
     *
     * @param null $field
     * @return array|string
     *
     * create by ck 20170717
     */
    public static function getLoginUserInfo($field = null)
    {
        static $auth;
        if (empty($auth)) {
            $auth = isset($_SESSION['auth']) ? $_SESSION['auth'] : [];
//            $auth = Di::getDefault()->get('session')->get('auth');
        }
        if (!$field) {
            return $auth;
        }
        if (isset($auth[$field])) {
            return $auth[$field];
        } else {
            return $field == 'userId' ? 0 : false;
        }
    }

    /**
     * 如有nodeId，则向视图传递位置信息
     *
     * @param null $nodeName
     * @param null $childNames
     * @param bool $showBack
     *
     * create by ck 20161017
     * modify by ck 20170706    可根据传入名称，生成面包屑，并带入子节点
     * modify by ck 20170904
     */
    public static function showNavPosition($nodeName = null, $childNames = null, $showBack = false)
    {
//        $view = Di::getDefault()->get('view');
//        if(!$nodeName){
//            $nodeId = Di::getDefault()->get('request')->getQuery('nodeId', 'int');
//            if($nodeId) {
//                $view->setVar('NavUl', XhsysTree::createNavUl($nodeId, $childNames, $showBack));
//            }
//        }
//        else{
//            $node = XhsysTree::findFirst("sNodeName = '{$nodeName}' AND iNodeType = 1 AND bDelete = 0");
//            if($node){
//                $view->setVar('NavUl', XhsysTree::createNavUl($node->iNodeId, $childNames, $showBack));
//            }
//        }
    }

    /**
     * 根据内容及错误代码调用js方法在顶层弹出提示
     *
     * @param string $content   --提示内容
     * @param bool $showTip     --是否显示弹框
     * @return string
     *
     * create by ck 20160414
     * modify by ck 20160722    加入参数说明是否返回文字提醒，防止未引入js文件导致无提示（不影响原方法使用）
     * modify by ck 20160918    文字及弹窗都显示，有错误代码则显示错误代码
     * modify by ck 20170221    去除错误代码
     * modify by ck 20170506    添加是否显示弹窗参数
     */
    public static function showMessageModal($content, $showTip = false)
    {
        $tip = '';
        if ($showTip) {
            $tip = "<script>window.top.xhTop.showMessage({title: '{$content}', type: 'error'});</script>";
        }
        return $content . $tip;
    }

    /**
     * 根据post数据获取字段值（去掉逗号前表名）
     *
     * @param $data
     * @return array
     *
     * create by ck 20160923
     */
    public static function getRealData($data)
    {
        $realData = array();
        foreach ($data as $key => $value) {
            $field = @end(explode(',', $key));
            $realData[$field] = is_string($value) ? trim($value) : $value;
        }
        return $realData;
    }

    /**
     * 根据节点名获取配置信息数组，如需及时则不经过缓存
     *
     * @param $configName
     * @param bool|false $timely
     * @return array|mixed
     *
     * create by ck 20161116
     * modify by ck 20170421    优化
     */
    public static function getConfigByName($configName, $timely = false)
    {
        if (!$configName) {
            return [];
        }
        if (!$timely && Cache::exists('config_' . $configName)) {
            return Cache::get('config_' . $configName);
        }
        $config = XhsysConfig::findFirst("sConfigName = '{$configName}' AND bDelete = 0");
        if (!$config) {
            return [];
        }
        $config or die(self::showMessageModal("无{$configName}配置信息！"));
        $config = json_decode($config->mConfigValue, true);
        Cache::save('config_' . $configName, $config);
        if (!$config) {
            return [];
        }
        return $config;
    }

    /**
     * 处理模糊搜索条件
     *
     * @param array $searchField
     * @param string $vague
     * @return array|string
     *
     * create by ck 20180912
     * modify by ck 20180915
     * modify by ck 20180918    支持单字符搜索(?)
     */
    public static function runVagueCondition(array $searchField, string $vague = null)
    {
        if (!$vague) {
            return '';
        }
        $conditions = [];
        //无逗号或空格则进行普通模糊搜索，否则批量精确查找
        if (strpos($vague, ',') !== false) {
            $vague = array_filter(explode(',', $vague));
            $vague = "'" . implode("','", $vague) . "'";
            foreach ($searchField as $field) {
                $conditions[] = "{$field} IN ({$vague})";
            }
        } elseif (strpos($vague, ' ') !== false) {
            $vague = array_filter(explode(' ', $vague));
            $vague = "'" . implode("','", $vague) . "'";
            foreach ($searchField as $field) {
                $conditions[] = "{$field} IN (" . $vague . ")";
            }
        } else {
            $vague = str_replace('?', '_', $vague);
            foreach ($searchField as $field) {
                $conditions[] = "{$field} LIKE '%" . $vague . "%'";
            }
        }
        //组合条件
        $conditions = implode(' OR ', $conditions);
//        if ($subQuery) {
            $conditions = " AND ({$conditions})";
//        }
        return $conditions;
    }

    /**
     * 根据传入的search节点，获取vague字段值
     * 对传入的字段名组成模糊查询字符串，并添加到条件中
     *
     * @param $conditions           --条件字符串
     * @param array $fieldArr       --模糊查询字段数组
     * @param array $searchArr      --查询条件
     * @param array $extraCondition --查询条件
     * @param bool $keepVague       --保持模糊搜索
     * @return bool
     *
     * create by ck 20160926
     * modify by ck 20160929    改成只针对条件字符串
     * modify by ck 20161017    条件为空则直接赋值条件
     * modify by ck 20161021    添加数组条件，当搜索字符不为中文时才搜索
     * modify by ck 20161101    去除中文，以数组自动判断，添加items判断，扩展搜索能力
     * modify by ck 20161218    解决字段中文搜索异常
     * modify by ck 20171203    可传入搜索条件
     */
    public static function runSearchVagueConditions(&$conditions, $fieldArr = [], $searchArr = [], $extraCondition = [], $keepVague = false)
    {
        if (empty($fieldArr)) {
            return false;
        }
        $searchArr or $searchArr = Di::getDefault()->get('request')->get('search');
        if (!isset($searchArr['vague']) || empty($searchArr['vague'])) {
            return false;
        }
        $vague = trim($searchArr['vague']);
        if (isset($searchArr['items'])) {
            $searchArr['items'] = array_filter($searchArr['items']);
            !empty($searchArr['items']) and $fieldArr = $searchArr['items'];
        }
        //有中文则去除时间类型字段，避免错误
        if (preg_match('/[\x{4e00}-\x{9fa5}]/u', $vague) > 0) {
            $unsetArr = ['tCreateDate'];
            foreach ($unsetArr as $unsetKey) {
                $index = array_search($unsetKey, $fieldArr);
                if ($index !== false) {
                    unset($fieldArr[$index]);
                }
            }
        }
        $conditionsArr = $extraCondition;
        //无逗号或空格则进行普通模糊搜索，否则批量精确查找
        if (strpos($vague, ',') !== false) {
            $vague = array_filter(explode(',', $vague));
            if ($keepVague) {
                foreach ($vague as $val) {
                    foreach ($fieldArr as $field) {
                        $conditionsArr[] = "{$field} LIKE '%" . trim($val) . "%'";
                    }
                }
            } else {
                $vague = "'" . implode("','", $vague) . "'";
                foreach ($fieldArr as $field) {
                    $conditionsArr[] = "{$field} IN ({$vague})";
                }
            }
        } elseif (strpos($vague, ' ') !== false) {
            $vague = array_filter(explode(' ', $vague));
            if ($keepVague) {
                foreach ($vague as $val) {
                    foreach ($fieldArr as $field) {
                        $conditionsArr[] = "{$field} LIKE '%" . trim($val) . "%'";
                    }
                }
            } else {
                $vague = "'" . implode("','", $vague) . "'";
                foreach ($fieldArr as $field) {
                    $conditionsArr[] = "{$field} IN (" . $vague . ")";
                }
            }
        } else {
            $vague = str_replace('?', '_', $vague);
            foreach ($fieldArr as $field) {
                $conditionsArr[] = "{$field} LIKE '%" . $vague . "%'";
            }
        }
        if (!$conditionsArr) {
            return false;
        }
        //组合条件
        $conditionsArr = implode(' OR ', $conditionsArr);
        if ($conditions) {
            $conditions .= " AND ($conditionsArr)";
        } else {
            $conditions = $conditionsArr;
        }
        return true;
    }

    /**
     * 从C++获取信息
     *
     * @param $configName   --配置文件节点名称
     * @param $funName      --方法名
     * @param $K            --参数数组
     * @return null
     *
     * create by ck 20160815
     * modify by ck 20161118
     * modify by ck 20161228    $configName可为数组，如果为数组则直接使用数组作为配置
     */
    public static function transitToCInterface($configName, $funName, $K)
    {
        if (is_array($configName)) {
            $config = $configName;
        } else {
            $config = self::getConfigByName($configName);
        }
        $config or die(self::showMessageModal('配置文件中缺少'.$configName.'节点', true));
        $transitClient = new TransitClient();
        $transitClient->connect($config['ip'], $config['port'])
        or die(self::showMessageModal('TCP端口连接失败，ip:'.$config['ip'].',port:'.$config['port'], true));
        $data = $result = null;
        $jsonDesc = array(
            'F'         => $funName,
            'clientIp'  => $_SERVER['REMOTE_ADDR'],
            'Z'         => 0
        );
        $jsonDesc = json_encode($jsonDesc);
        $jsonData = json_encode($K);
        $transitClient->writeAndReadWebSSReq($jsonDesc, $jsonData, $data, $result);
        ($data && $data != 'null') or die(self::showMessageModal('TCP无返回，请检查！', true));
        return $data;
    }

    /**
     * 从C++获取信息
     *
     * @param $configName       --配置文件节点名称
     * @param $funName          --方法名
     * @param $requestData      --参数数组
     * @return null
     * @throws \Exception
     *
     * create by ck 20160815
     * modify by ck 20161118
     * modify by ck 20161228    $configName可为数组，如果为数组则直接使用数组作为配置
     * modify by ck 20170803    遇错误则抛出异常
     */
    public static function transitToCppInterface($configName, $funName, $requestData)
    {
        if (is_array($configName)) {
            $config = $configName;
        } else {
            $config = self::getConfigByName($configName);
        }
        if (!$config) {
            throw new \Exception("配置文件中缺少{$configName}节点");
        };
        $transitClient = new TransitClient();
        if (!$transitClient->connect($config['ip'], $config['port'])) {
            throw new \Exception("TCP端口连接失败，ip:{$config['ip']},port:{$config['port']}");
        }
        $data = $result = null;
        $jsonDesc = array(
            'F' => $funName,
            'clientIp' => $_SERVER['REMOTE_ADDR'],
            'Z' => 0
        );
        $transitClient->writeAndReadWebSSReq(json_encode($jsonDesc), json_encode($requestData), $data, $result);
        if (!$data || $data == 'null') {
            throw new \Exception('TCP无返回，请检查！');
        }
        if (!json_decode($data)) {
            throw new \Exception('返回数据格式非json！');
        }
        return json_decode($data, true);
    }

    /**
     * 根据数组，id字段替换用户id为用户姓名
     *
     * @param $obj
     * @param $fields
     * @param $isArray
     *
     * create by ck 20170329
     * modify by ck 20170628    结构更改
     */
    public static function runUserNameById(&$obj, $fields, $isArray)
    {
        $userIdArr = [];
        is_string($fields) and $fields = [$fields => $fields];
        if ($isArray) {
            foreach ($fields as $idField => $nameField) {
                $userIdArr = array_merge($userIdArr, array_column($obj, $idField));
            }
            $userNameArr = XhsysUser::getUserNameById($userIdArr);
            foreach ($obj as $index => $objOne) {
                foreach ($fields as $idField => $nameField) {
                    if (!isset($userNameArr[$objOne[$idField]])) continue;
                    $obj[$index][$nameField] = $userNameArr[$objOne[$idField]];
                }
            }
        } else {
            foreach ($fields as $idField => $nameField) {
                $userIdArr = array_merge($userIdArr, $obj[$idField]);
            }
            $userNameArr = XhsysUser::getUserNameById($userIdArr);
            foreach ($fields as $idField => $nameField) {
                if (!isset($userNameArr[$obj[$idField]])) continue;
                $obj[$nameField] = $userNameArr[$obj[$idField]];
            }
        }
    }

    /**
     * 转换用户姓名
     *
     * @param $obj
     * @param $fields
     * @param bool $multiple
     * @return bool
     *
     * create by ck 20171122
     */
    public static function runUserName(&$obj, $fields, $multiple = false)
    {
        if (!$obj || !$fields) {
            return false;
        }
        $userIdArr = [];
        is_string($fields) and $fields = [$fields => $fields];
//        if($isArray){
        foreach ($fields as $idField => $nameField) {
            $userIds = array_column($obj, $idField);
            if ($multiple) {
                $userIds = implode(',', $userIds);
                $userIds = explode(',', $userIds);
                $userIds = array_filter($userIds);
            }
            $userIdArr = array_merge($userIdArr, $userIds);
        }
        $userNameArr = XhsysUser::getUserNameById($userIdArr);
        foreach ($obj as $index => &$objOne) {
            foreach ($fields as $idField => $nameField) {
                if ($multiple) {
                    $ids = explode(',', trim($objOne[$idField], ','));
                    $names = [];
                    foreach ($ids as $userId) {
                        if (!isset($userNameArr[$userId])) {
                            continue;
                        }
                        $names[] = $userNameArr[$userId];
                    }
                    $objOne[$nameField] = implode(',', $names);
                } else {
                    if (!isset($userNameArr[$objOne[$idField]])) {
                        continue;
                    }
                    $objOne[$nameField] = $userNameArr[$objOne[$idField]];
                }
            }
        }
//        }
//        else{
//            foreach($fields as $idField => $nameField){
//                $userIds = $obj[$idField];
//                if ($multiple) {
//                    $userIds = explode(',', trim($obj[$idField], ','));
//                }
//                $userIdArr = array_merge($userIdArr, $userIds);
//            }
//            $userNameArr = XhsysUser::getUserNameById($userIdArr);
//            foreach($fields as $idField => $nameField){
//                if ($multiple) {
//                    $ids = explode(',', trim($obj[$idField], ','));
//                    $names  = [];
//                    foreach ($ids as $userId) {
//                        if (!isset($userNameArr[$userId])) continue;
//                        $names[] = $userNameArr[$userId];
//                    }
//                    $obj[$nameField] = implode(',', $names);
//                }
//                else {
//                    if(!isset($userNameArr[$obj[$idField]])) continue;
//                    $obj[$nameField] =  $userNameArr[$obj[$idField]];
//                }
//            }
//        }
        return true;
    }

    /**
     * 转换用户登陆名
     *
     * @param $obj
     * @param $fields
     * @return bool
     *
     * create by ck 20180518
     */
    public static function runUserLogonName(&$obj, $fields)
    {
        if (!$obj || !$fields){
            return false;
        }
        $userIdArr = [];
        is_string($fields) and $fields = [$fields => $fields];
        foreach($fields as $idField => $nameField){
            $userIds   = array_column($obj, $idField);
            $userIdArr = array_merge($userIdArr, $userIds);
        }
        $userLogonName = XhsysUser::getUserLogonName($userIdArr);
        foreach($obj as $index => &$objOne){
            foreach($fields as $idField => $nameField){
                if (!isset($userLogonName[$objOne[$idField]])) continue;
                $objOne[$nameField] = $userLogonName[$objOne[$idField]];
            }
        }
        return true;
    }

    /**
     * 根据用户id拼接学校id
     *
     * @param $obj
     * @param $userIdField
     * @param $schoolIdField
     * @return bool
     *
     * create by ck 20180130
     */
    public static function runUserSchoolId(&$obj, $userIdField, $schoolIdField)
    {
        $userIds = [];
        foreach ($obj as $row) {
            $userIds[] = $row[$userIdField];
        }
        if (!$userIds) {
            return false;
        }
        $userSchool = XhsysUser::find([
            'columns'    => 'iUserId,iSchoolId',
            'conditions' => 'iUserId IN ({idArr:array})',
            'bind'       => ['idArr' => $userIds]
        ])->toArray();
        $userSchool = array_column($userSchool, 'iSchoolId', 'iUserId');
        foreach ($obj as &$row) {
            $row[$schoolIdField] = $userSchool[$row[$userIdField]];
        }
        unset($row);
        return true;
    }

    /**
     * 根据用户id拼接班级名称
     *
     * @param $obj
     * @param $userIdField
     * @param $classNameField
     * @return bool
     *
     * create by ck 20190123
     */
    public static function runUserClassName(&$obj, $userIdField, $classNameField)
    {
        $userIds = [];
        foreach ($obj as $row) {
            $userIds[] = $row[$userIdField];
        }
        if (!$userIds) {
            return false;
        }
        $classNames = XhsysUser::find([
            'columns'    => 'iUserId,sAdClsName',
            'conditions' => 'iUserId IN ({idArr:array})',
            'bind'       => ['idArr' => $userIds]
        ])->toArray();
        $classNames = array_column($classNames, 'sAdClsName', 'iUserId');
        foreach ($obj as &$row) {
            $row[$classNameField] = $classNames[$row[$userIdField]];
        }
        unset($row);
        return true;
    }

    /**
     * 根据数组，id字段替换学校id字符串为学校名称
     *
     * @param $obj
     * @param array $fields
     * @param bool $isArray
     *
     * create by ck 20170329
     * modify by ck 20170426    fields字段改为关联数组形式 ['iSchoolId' => 'sSchoolName']
     */
    public static function runSchoolNameByIdStr(&$obj, $fields = [], $isArray = true)
    {
        $schoolNameArr = XhschoolInfo::getSchoolArray();
        if($isArray){
            foreach($obj as $index => $objOne){
                foreach($fields as $idField => $nameField){
                    if(!$objOne[$idField]) continue;
                    $schIdArr = explode(',', trim($objOne[$idField], ','));
                    $schNameArr = [];
                    foreach($schIdArr as $schId){
                        isset($schoolNameArr[$schId]) and $schNameArr[] = $schoolNameArr[$schId];
                    }
                    $obj[$index][$nameField] = implode(',', $schNameArr);
                }
            }
        }
        else{
            foreach($fields as $idField => $nameField){
                if(!$obj[$idField]) continue;
                $schIdArr = explode(',', trim($obj[$idField], ','));
                $schNameArr = [];
                foreach($schIdArr as $schId){
                    isset($schoolNameArr[$schId]) and $schNameArr[] = $schoolNameArr[$schId];
                }
                $obj[$nameField] = implode(',', $schNameArr);
            }
        }
    }

    /**
     * 根据数组或对象查询枚举表及配置文件，替换枚举值成枚举名称
     *
     * @param $obj
     * @return bool
     *
     * create by ck 20160602
     * modify by ck 20160603
     * modify by ck 20161020    结构更改，使用缓存查询
     * modify by ck 20170406    添加固定枚举
     */
    public static function replaceEnumFiled(&$obj)
    {
        if(!$obj) {
            return false;
        }
        //枚举数据库替换
        $enumListArr = EnumList::getFieldEnumId();
        $arrOne = $obj[0];
        $fieldArr = $enumCodeArr = array();
        foreach($arrOne as $fieldName => $fieldValue){
            if(array_key_exists($fieldName, $enumListArr)) {
                $fieldArr[$fieldName] = $enumListArr[$fieldName];
            }
        }
        if(!empty($fieldArr)) {
            foreach($fieldArr as $enumNodeId){
                $enumCodeArr[$enumNodeId] = XhsysEnum::getEnumByNodeId($enumNodeId);
            }
            foreach($obj as $key => $value){
                foreach($fieldArr as $k=>$v){
                    if(isset($enumCodeArr[$v][$obj[$key][$k]])) {
                        $obj[$key][$k] = $enumCodeArr[$v][$obj[$key][$k]];
                    }
                }
            }
        }
        //固定枚举替换
        $enumListArr = EnumList::getFieldEnumList();
        $arrOne = $obj[0];
        $fieldArr = array();
        foreach($arrOne as $fieldName => $fieldValue){
            if(array_key_exists($fieldName, $enumListArr)) {
                $fieldArr[$fieldName] = $enumListArr[$fieldName];
            }
        }
        if(!empty($fieldArr)) {
            foreach($obj as $key => $value){
                foreach($fieldArr as $k => $v){
                    if(isset($v[$value[$k]])){
                        $obj[$key][$k] = $v[$value[$k]];
                    }
                }
            }
        }
        return true;
    }

    /**
     * 根据传入page及rows，计算偏移量并以数组形式返回，如果传入名称，则返回相应数据
     *
     * @param null $name
     * @return array|int
     *
     * create by ck 20160927
     */
    public static function getPageNumInfo($name = null)
    {
        static $pageInfo;
        if(empty($pageInfo) || !isset($pageInfo[$name])){
            $request  = Di::getDefault()->get('request');
            $page     = $request->getPost('page', 'int', 1);                     //获取页码，默认1
            $rows     = $request->getPost('rows', 'int', 0);                     //获取行数，默认0行
            $offset   = $rows * ($page - 1);
            $pageInfo = array(
                'page'      => $page,
                'rows'      => $rows,
                'offset'    => $offset
            );
        }
        return isset($pageInfo[$name]) ? $pageInfo[$name] : $pageInfo;
    }


    /**
     * 根据QueryBuilder分页，默认第一页，50条记录，组合成easyui datagrid格式返回  不需要枚举 只用于项目管理
     *
     * @param $builder
     * @param bool|false $return    --是否返回
     * @return string|array
     *
     * create by zqk 20180420
     */


    public static function runBuilderPages($builder, $return = false)
    {
        $paginator = new QueryBuilder(
            array(
                'builder'   => $builder,
                'limit'     => self::getPageNumInfo('rows') ?: 50,
                'page'      => self::getPageNumInfo('page')
            )
        );
        @$paginator = $paginator->getPaginate();
        $total = $paginator->total_items;
        $data  = $paginator->items->toArray();
        $result = ['total' => $total, 'rows' => $data];
        if($return) return $result;
        return json_encode($result, JSON_UNESCAPED_UNICODE);
    }





    /**
     * 根据模型及条件，计算total值，更改条件后查询
     * 替换查询到的数据枚举值并显示
     * 如需返回则将数组返回（用于下面操作）
     *
     * @param $modelName            --模型名称
     * @param array $parameters     --条件数组
     * @param bool|false $return    --是否返回
     * @return string|array
     *
     * create by ck 20160914
     * modify by ck 20160923    增加参数指定是否返回数据
     * modify by ck 20161129    总数优化判断
     * modify by ck 20170426    添加pagesize判断
     */
    public static function runModelPaginator($modelName, $parameters = array(), $return = false)
    {
        $total = $modelName::count($parameters);
        $total = is_int($total) ? $total : count($total->toArray());
        if(self::getPageNumInfo('rows')){
            $parameters['limit'] = array(
                'number'    => self::getPageNumInfo('rows'),
                'offset'    => self::getPageNumInfo('offset')
            );
        }
        $data = $modelName::find($parameters)->toArray();
        self::replaceEnumFiled($data);
        $result = ['total' => $total, 'rows' => $data];
        if($return) return $result;
        return json_encode($result, JSON_UNESCAPED_UNICODE);
    }

    /**
     * 根据模型及条件，计算total值，更改条件后查询，并替换枚举
     *
     * @param $modelName
     * @param array $parameters
     * @param array $enumArr
     * @return array
     *
     * create by ck 20170629
     */
    public static function runModelPage($modelName, $parameters = [], $enumArr = [])
    {
        $total = $modelName::count($parameters);
        $total = is_int($total) ? $total : count($total->toArray());
        if(self::getPageNumInfo('rows')){
            $parameters['limit'] = array(
                'number'    => self::getPageNumInfo('rows'),
                'offset'    => self::getPageNumInfo('offset')
            );
        }
        $rows = $modelName::find($parameters)->toArray();
        //处理枚举
        if($enumArr){
            Tool::runEnum($rows, $enumArr);
        }
        $result = ['total' => $total, 'rows' => $rows];
        return $result;
    }

    /**
     * 根据QueryBuilder分页，默认第一页，50条记录，组合成easyui datagrid格式返回
     *
     * @param $builder
     * @param bool|false $return    --是否返回
     * @return string|array
     *
     * create by ck 20160926
     * modify by ck 20161108
     */
    public static function runBuilderPaginator($builder, $return = false)
    {
        $paginator = new QueryBuilder(
            array(
                'builder'   => $builder,
                'limit'     => self::getPageNumInfo('rows') ?: 50,
                'page'      => self::getPageNumInfo('page')
            )
        );
        @$paginator = $paginator->getPaginate();
        $total = $paginator->total_items;
        $data  = $paginator->items->toArray();
        self::replaceEnumFiled($data);
        $result = ['total' => $total, 'rows' => $data];
        if($return) return $result;
        return json_encode($result, JSON_UNESCAPED_UNICODE);
    }

    /**
     * 根据QueryBuilder分页，默认第一页，50条记录，组合成easyui datagrid格式返回
     *
     * @param $builder
     * @param array $enumArr
     * @return array
     *
     * create by ck 20170720
     */
    public static function runBuilderPage($builder, $enumArr = [])
    {
        $paginator = new QueryBuilder([
            'builder'   => $builder,
            'limit'     => self::getPageNumInfo('rows') ?: 10000,
            'page'      => self::getPageNumInfo('page')
        ]);
        @$paginator = $paginator->getPaginate();
        $total = $paginator->total_items;
        $rows  = $paginator->items->toArray();
        //处理枚举
        if($enumArr){
            Tool::runEnum($rows, $enumArr);
        }
        $result = ['total' => $total, 'rows' => $rows];
        return $result;
    }

    /**
     * 根据phql语句数组，替换其中 :columns: 字符为 count(*) 获取总记录数
     * 并将执行语句 :columns: 字符替换为需查询字段查询，组成json后返回
     *
     * @param array $queryArr     --查询数组，共query（执行语句，:columns:占位符）及columns（需查询字段数组）两个节点
     * @return string
     *
     * create by ck 20160927
     */
    public static function runPhqlPaginator($queryArr)
    {
        $modelsManager = Di::getDefault()->get('modelsManager');
        $offset = self::getPageNumInfo('offset');
        $rows   = self::getPageNumInfo('rows');
        $query  = str_replace(':columns:', 'count(*)', $queryArr['query']);
        $total  = $modelsManager->executeQuery($query)->toArray();
        $total  = get_object_vars($total[0])[0];
        $query  = str_replace(':columns:', implode(',', $queryArr['columns']), $queryArr['query']);
        $query .= " LIMIT {$offset},{$rows}";
        $rows   = $modelsManager->executeQuery($query)->toArray();
        self::replaceEnumFiled($rows);
        return json_encode(['total' => $total, 'rows' => $rows]);
    }

    /**
     * 根据原生sql语句执行分页操作
     *
     * @param $queryArr             --查询数组，共query（执行语句，:columns:占位符）及columns（需查询字段数组）两个节点
     * @param null $db              --数据库连接
     * @param bool|false $return    --是否返回数据
     * @return string
     *
     * create by ck 20160930
     * modify by ck 20161212    增加数据库连接可选，添加返回参数，优化结构
     */
    public static function runSqlPaginator($queryArr, $db = null, $return = false)
    {
        $db or $db = Di::getDefault()->get('db');
        $offset = self::getPageNumInfo('offset');
        $rows   = self::getPageNumInfo('rows');
        $query  = str_replace(':columns:', 'count(*) as count', $queryArr['query']);
//        $total  = $db->query($query);
//        $total->setFetchMode(Phalcon\Db::FETCH_NUM);
//        $total  = $total->fetch()[0];
        if(stristr($query, 'group by')){
            $query = "select count(*) as count from ({$query}) as t";
        }
        $total = $db->fetchOne($query);
//        var_dump($total);die;
        $total = $total['count'];
        $query  = str_replace(':columns:', is_array($queryArr['columns']) ? implode(',', $queryArr['columns']) : $queryArr['columns'], $queryArr['query']);
        $query .= " LIMIT {$offset},{$rows}";
//        $rows   = $db->query($query);
//        $rows->setFetchMode(Phalcon\Db::FETCH_ASSOC);
//        $rows   = $rows->fetchAll();
        $rows   = $db->fetchAll($query);
        self::replaceEnumFiled($rows);
        $result = ['total' => $total, 'rows' => $rows];
        if($return) return $result;
        return json_encode(['total' => $total, 'rows' => $rows]);
    }

    /**
     * 根据原生sql语句执行分页操作
     *
     * @param $sql                  --查询数组，共query（执行语句，:columns:占位符）及columns（需查询字段数组）两个节点
     * @param array $enumArr        --枚举
     * @param null $db              --数据库连接
     * @return array
     * @throws XhException
     *
     * create by ck 20170927
     * modify by ck 20171115
     * modify by ck 20171212    group by查询逻辑更改
     * modify by ck 20180912    扩展，可解析sql语句
     */
    public static function runSqlPage($sql, $enumArr = [], $db = null)
    {
        if (!$db) {
            $db  = Di::getDefault()->get('db');
        }
        $offset = self::getPageNumInfo('offset');
        $size   = self::getPageNumInfo('rows');
        if (is_array($sql)) {
            $query = str_replace(
                ':columns:',
                is_array($sql['columns']) ? implode(',', $sql['columns']) : $sql['columns'],
                $sql['query']
            );
            if (stristr($sql['query'], 'group by')) {
                $totalQuery = "select count(*) as total from ({$query}) as t";
            } else {
                $totalQuery = str_replace(':columns:', 'count(*) as total', $sql['query']);
            }
        } else {
            $query = $sql;
            if (!preg_match('/^select(.*) from/is', $sql, $columns)) {
                throw new XhException('sql语句解析失败', $sql);
            }
            if (stristr($sql, 'group by')) {
                $totalQuery = "select count(*) as total from ({$sql}) as t";
            } else {
                $totalQuery = preg_replace(
                    '/^select (.*) from/is',
                    'select count(*) as total from',
                    $sql,
                    1
                );
//                $totalQuery = str_replace($columns[1], 'count(*) as total', $sql);
            }
        }
        $total = $db->fetchOne($totalQuery)['total'];
        if ($size) {
            $query .= " LIMIT {$offset},{$size}";
        }
        $rows = $db->fetchAll($query);
        //处理枚举
        if ($enumArr) {
            Tool::runEnum($rows, $enumArr);
        }
        $result = ['total' => $total, 'rows' => $rows];
        return $result;
    }

    /**
     * 根据应用编号及学校ID调用C++接口刷新书架
     *
     * @param $appCode      --应用编号
     * @param $schoolId     --学校ID
     * @return bool|string  --失败原因或true
     *
     * create by ck 20160810
     */
    public static function refreshBookshelf($appCode, $schoolId)
    {
        static $transitClient,$bookConfig;
        if(empty($transitClient)){
            $transitClient = new TransitClient();
            $bookConfig = self::getConfigByName('cBookShelf');
            $state = $transitClient->connect($bookConfig['ip'], $bookConfig['port']);
            if(!$state) {
                Main::createCommitLog('app', 'AppContactQBank', 'refreshErr', $bookConfig, $schoolId, "书架刷新失败！原因：连接TCP失败");
                return self::showMessageModal('书架刷新失败！原因：连接TCP失败');
            }
        }
        $data = $result = null;
        $jsonDesc = json_encode(array(
            'F'         => $bookConfig['funName'],
            'clientIp'  => $_SERVER['REMOTE_ADDR'],
            'Z'         => 0,
        ));
        $jsonData = json_encode(array(
            'C'         => $appCode,
            'iSchoolId' => $schoolId
        ));
        $transitClient->writeAndReadWebSSReq($jsonDesc, $jsonData, $data, $result);
        if(!$data) {
            Main::createCommitLog('app', 'AppContactQBank', 'refreshErr', $jsonData, $schoolId, '书架刷新失败！原因：TCP返回为空');
            return self::showMessageModal('书架刷新失败！原因：TCP返回为空');
        }
        $data = json_decode($data, true);
        if($data['R'] != 200) {
            Main::createCommitLog('app', 'AppContactQBank', 'refreshErr', $jsonData, $schoolId, "书架刷新失败！原因：{$data['M']}");
            return self::showMessageModal("书架刷新失败！原因：{$data['M']}");
        }
        return true;
    }

    /**
     * 刷新用户登陆
     *
     * @param array $userIds
     * @return bool
     * @throws \Exception
     *
     * create by ck 20180125
     */
//    public static function refreshUserLogin($userIds = [])
//    {
//        if (!$userIds) {
//            throw new \Exception('缺少用户id');
//        }
//        $url = Main::getConfigByName('cLogin')['url'];
//        if (!$url){
//            throw new \Exception('缺少url参数');
//        }
//        $httpClient = new HttpClient($url);
//        $response = $httpClient->post('/refreshUserInfo',
//            ['K' => json_encode(['type' => 'batch', 'oUserId' => implode(',', $userIds)])]);
//        $body = $response->toArray();
//        if (!isset($body['R']) || $body['R'] != 200){
//            throw new \Exception($body['I'] ?: $response->body);
//        }
//        return true;
//    }

    /**
     * 上传文件，成功则返回url，否则返回false
     * 如需及时链接，则返回不同链接
     *
     * @param $filePath             --文件路径
     * @param $key                  --key值
     * @param string $fileType      --文件类型
     * @param bool|false $https     --是否使用https链接
     * @param bool $showMd5
     * @return bool
     *
     * create by ck 20160918
     * modify by ck 20160924
     * modify by ck 20161018    如无文件则尝试GBK编码转换为UTF-8
     * modify by ck 20161101    去除md5输入，增加上传记录，并增加文件类型参数
     * modify by ck 20161104    如果需https链接则返回相应链接
     * modify by ck 20170728    优化，使用httpClient类
     */
    public static function uploadToFileManageSystem($filePath, $fileType = 'other', $key = null, $https = false, $showMd5 = false)
    {
        $fileConfig = self::getConfigByName('fileServer');
        if((isset($fileConfig['useOSS']) && $fileConfig['useOSS'] == 'true') || filesize($filePath) > 1024 * 1024 * 5) { //大于5M走OSS
            return self::uploadFileToOSS($filePath, $fileType, $key, $https, $showMd5);
        }
        if(!file_exists($filePath)){
            $filePath = iconv('UTF-8', 'GBK', $filePath);
            if(!file_exists($filePath)) {
                self::createCommitLog('main', 'upload', 'fileErr', $filePath, null, '文件不存在');
                return false;
            }
        }
        $filePath   = realpath($filePath);
        $key        = $key ?: $fileConfig['key'];
        $md5Str     = strtoupper(md5_file($filePath));
        $httpClient = new HttpClient($fileConfig['url']);
        $httpClient->setHeader('md5', $md5Str);
        $response   = $httpClient->post(sprintf('file/upload/%s/%s', $key, $md5Str), ['files' => new \CURLFile($filePath)]);
        $info       = $response->toObject();
        if($info->status != 1000){
            self::createCommitLog('main', 'upload', 'uploadErr', $response, null, '上传失败');
            return false;
        }
        $url = $info->uploadFileDTO->fileId;
//        $uploadUrl  = $fileConfig['url'] . 'file/upload/'. $key . '/' . $md5Str;
//        $ch = curl_init();
//        $post_data = array(
////            'files' => '@'. $filePath
////            'files' => curl_file_create($filePath)
//            'files' => new \CURLFile($filePath)
//        );
//        curl_setopt($ch, CURLOPT_HEADER, false);
//        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
//            'md5:'. $md5Str
//        ));
//        curl_setopt($ch, CURLOPT_POST, true);
//        curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
//        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);     //不自动输出
//        curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
//        curl_setopt($ch, CURLOPT_URL, $uploadUrl);
//        $fileInfo = curl_exec($ch);
//        curl_close($ch);
//        @unlink($filePath);
//        $info = json_decode($fileInfo, true);
//        if($info['status'] != 1000){
//            self::createCommitLog('main', 'upload', 'uploadErr', $fileInfo, null, '上传失败');
//            return false;
//        }
//        $url = $info['uploadFileDTO']['fileId'];
        if($https) {
            $url = str_replace('http://', 'https://', $url);
        }
        if($showMd5){
            $url .= '?md5=' . $md5Str;
        }
        //新增文件上传记录
        $fileName   = explode(DIRECTORY_SEPARATOR, $filePath);
        $fileName   = end($fileName);
        $fileSuffix = explode('.', $fileName);
        $fileSuffix = end($fileSuffix);
        XhsysUploadfile::createRecord($fileName, $fileType, $fileSuffix, $md5Str, filesize($filePath), $url, $key);
        return $url;
    }

    /**
     * 上传文件到OSS，成功则返回url，否则返回false
     *
     * @param $filePath
     * @param string $fileType
     * @param null $key
     * @param bool $https
     * @param bool $showMd5
     * @return bool|mixed
     *
     * create by ck 20170607
     * modify by ck 20170619    添加文件服务器接入
     * modify by ck 20170704    更改解密算法
     * modify by ck 20171211    忽略文件服务器请求失败
     */
    public static function uploadFileToOSS($filePath, $fileType = 'other', $key = null, $https = false, $showMd5 = false)
    {
        if(!file_exists($filePath)){
            $filePath = iconv('UTF-8', 'GBK', $filePath);
            if(!file_exists($filePath)) {
                self::createCommitLog('main', 'upload', 'fileErr', $filePath, null, '文件不存在');
                return false;
            }
        }
        $fileConfig = self::getConfigByName('fileServer');
        $filePath   = realpath($filePath);
        $fileName   = basename($filePath);
        $fileSuffix = explode('.', $fileName);
        $fileSuffix = end($fileSuffix);
        $key        = $key ? $key : $fileConfig['key'];
        $md5Str     = strtoupper(md5_file($filePath));

        //请求文件服务器
//        $httpClient = (new HttpClient())->setBaseUrl($fileConfig['url'])->setTimeout(0);
//        $response   = $httpClient->post("/file/upload/largefile/{$key}/{$md5Str}/{$fileSuffix}");
//        if(!$response->isSuccess()){
//            self::createCommitLog('main', 'upload', 'uploadErr', $response->body, null, '请求文件服务器失败');
//            return false;
//        }
//        $body = $response->toObject();
//        if(!$body->uploadFileDTO->fileId){
//            self::createCommitLog('main', 'upload', 'uploadErr', $body, null, 'fileId不存在');
//            return false;
//        }

        $ossAccess  = $fileConfig['ossAccess'];
//        $crypt      = Di::getDefault()->get('crypt');
//        $crypt->setKey(rtrim($crypt->getKey(), 'OSS') . 'OSS');
//        $ossAccess  = $crypt->decryptBase64($ossAccess);
        $ossAccess  = openssl_decrypt($ossAccess, 'AES-256-CBC', rtrim(Di::getDefault()->get('config')->get('key'),'OSS').'OSS', 0, '1111111111111111');
        $ossAccess  = explode('+', $ossAccess);
        try {
            $ossClient   = new OssClient($ossAccess[0], $ossAccess[1], $ossAccess[2]);
//            $ossFileName = basename($body->uploadFileDTO->fileId ?: ($md5Str . '.' . $fileSuffix));
            $ossFileName = basename($md5Str . '.' . $fileSuffix);
            $result      = $ossClient->uploadFile($ossAccess[3], $key . '/' . $ossFileName, $filePath);
            if(!isset($result['oss-request-url'])) return false;
            $url     = $result['oss-request-url'];
            $https   and $url = str_replace('http://', 'https://', $url);
            $showMd5 and $url .= '?md5=' . $md5Str;
            XhsysUploadfile::createRecord($fileName, $fileType, $fileSuffix, $md5Str, filesize($filePath), $url, $key);

            return $url;
        } catch (OssException $e) {
            self::createCommitLog('main', 'upload', 'uploadErr', $e->getMessage(), null, '上传失败');
            return false;
        }
    }

    /**
     * 创建操作日志
     *
     * @param $module           --模块名
     * @param $subModule        --子模块
     * @param $operate          --操作类型（add/edit/del/contact）
     * @param $content          --model内容（json格式）
     * @param null $contentId   --modelID
     * @param null $explain     --说明
     * @return bool
     *
     * create by ck 20160914
     */
    public static function createCommitLog($module, $subModule, $operate, $content, $contentId = null, $explain = null){
        $log = new XhlogCommit();
        $log->sModule       = $module;
        $log->sSubmodule    = $subModule;
        $log->sOperate      = $operate;
        $log->mContent      = (is_object($content) || is_array($content) ) ? json_encode($content, JSON_UNESCAPED_UNICODE) : $content;
        $log->iContentId    = $contentId ?: 0;
        $log->sExplain      = $explain;
        $log->tCreateDate   = date('Y-m-d H:i:s');
        $log->iUserId       = Main::getLoginUserInfo('userId');
        $log->sUserIP       = Tool::getIp();
        $log->sUserOS       = Tool::getOS();
        $log->sUserBrowser  = Tool::getBrowser();
        return $log->save();
    }

    /**
     * 创建错误日志
     *
     * @param $dispatcher       --调度类（获取信息）
     * @param $errorInfo        --错误信息（数组格式，转json储存）
     * @return bool
     *
     * create by ck 20160918
     */
    public static function createErrorLog(Dispatcher $dispatcher, $errorInfo){
        $log = new XhlogError();
        $log->sControllerName   = $dispatcher->getControllerClass();
        $log->sMethodName       = $dispatcher->getActiveMethod();
        $log->mRequestParam     = json_encode($_REQUEST, JSON_UNESCAPED_UNICODE);
        $log->mErrorMessage     = json_encode($errorInfo, JSON_UNESCAPED_UNICODE);
        $log->iUserId           = User::getId();
        $log->tCreateDate       = date('Y-m-d H:i:s');
        $log->sUserIP           = $_SERVER['REMOTE_ADDR'];
        $log->sUserOS           = Tool::getOS();
        $log->sUserBrowser      = Tool::getBrowser();
        return $log->save();
    }

    /**
     * 创建错误信息，以json格式返回
     *
     * @param $code
     * @param string $message
     * @param bool|false $show
     * @return string
     *
     * create by ck 20161009
     */
    public static function runErrorInterfaceInfo($code, $message = '', $show = false){
        $result = [];
        $result['R'] = $code;
        if($show === true){
            $result['I'] = $message;
        }
        elseif($show === false){
            $result['I'] = '服务器内部错误';
        }
        $result['M'] = $message;
        return json_encode($result, JSON_UNESCAPED_UNICODE);
    }

    /**
     * 创建成功信息，以json格式返回
     *
     * @param string $data
     * @return string|array
     *
     * create by ck 20161009
     */
    public static function runSuccessInterfaceInfo($data = '')
    {
        $result = [];
        $result['R'] = 200;
        $result['D'] = $data;
        $result['I'] = '成功';
        $result['M'] = '成功';
        return json_encode($result, JSON_UNESCAPED_UNICODE);
    }

    /**
     * 根据路径解析应用信息（处理基础信息，调用方法具体解析）
     *
     * @param $filePath
     * @return array
     * @throws \Exception
     *
     * create by ck 20170609
     * modify by ck 20170727    添加多个默认值
     */
    public static function parseAppInfo($filePath)
    {
        if(!file_exists($filePath)){
            throw new \Exception('文件不存在！');
        }
        $uploadResult = self::uploadToFileManageSystem($filePath, 'app', 'XHSVADAPBUABML');
        if(!$uploadResult) {
            @unlink($filePath);
            throw new \Exception('文件上传失败！');
        }
        $fileName   = basename($filePath);
        $fileSuffix = explode('.', $fileName);
        $fileSuffix = end($fileSuffix);
        $appInfo    = [
            'name'              => $fileName,
            'md5'               => strtoupper(md5_file($filePath)),
            'url'               => $uploadResult,
            'packageSize'       => filesize($filePath),
            'show'              => 'true',
            'installPosition'   => 1,
            'outside'           => 'false',
            'updateLevel'       => 2,
        ];
        switch($fileSuffix){
            case 'apk': {
                $appInfo['platform']    = 'android';
                $appInfo['independent'] = 'false';
                $appInfo = self::parseAndroidInfo($filePath, $appInfo);
                break;
            }
            case 'ipa': {
                $appInfo['platform']    = 'ios';
                $appInfo['independent'] = 'true';
                $appInfo = self::parseIOSInfo($filePath, $appInfo);
                break;
            }
            default: throw new \Exception('文件类型不正确！');
        }
        $endStr = substr($appInfo['packageName'], -3);
        switch($endStr){
            case 'stu': $appInfo['userType'] = 1; break;
            case 'tch': $appInfo['userType'] = 2; break;
            case 'pre': $appInfo['userType'] = 3; break;
            case 'unc': $appInfo['userType'] = 4; break;
            default: $appInfo['userType'] = 4;
        }
        @unlink($filePath);
        return $appInfo;
    }



    /**
     * 解析Android应用信息并返回
     *
     * @param $fullPath
     * @param $appInfo
     * @return array
     * @throws \Exception
     *
     * create by ck 20170609
     * modify by ck 20181123
     */
    private static function parseAndroidInfo($fullPath, $appInfo)
    {
        $parseConfig = self::getConfigByName('appParse');
        $aaptPath    = isset($parseConfig['aaptPath']) ? $parseConfig['aaptPath'] : '';

        //执行aapt命令
        $command = "{$aaptPath}aapt dump badging {$fullPath} 2>&1";
        exec($command, $arr, $var);
        if($var !== 0) {
            @unlink($fullPath);
            throw new \Exception("aapt执行失败:({$command})" . json_encode($arr));
        }

        //获取包名，应用名，版本号，版本名，图标路径等信息
        $appInfoStr = json_encode($arr, JSON_UNESCAPED_UNICODE);
        preg_match("/package: name='(.*?)'/", $appInfoStr, $package);
        $appInfo['packageName'] = end($package);
        preg_match("/package: (.*?)versionCode='(.*?)'/", $appInfoStr, $package);
        $appInfo['versionCode'] = end($package);
        preg_match("/package: (.*?)versionName='(.*?)'/", $appInfoStr, $package);
        $appInfo['versionName'] = end($package);
        preg_match("/application: label='(.*?)'/", $appInfoStr, $package);
        $appInfo['name'] = end($package);
        preg_match_all("/application-icon-([0-9]+):'(.*?)'/", $appInfoStr, $package);
        $iconZipPath = @stripslashes(end(end($package)));

        //提取图标并上传，打开失败则直接返回
        if ($iconZipPath) {
            $zip = new \ZipArchive;
            if ($zip->open($fullPath) !== true) {
                @unlink($fullPath);
                throw new \Exception('文件解压失败！');
            }
            $zip->extractTo(TEMP_PATH, $iconZipPath);
            $zip->close();
            $iconPath = Tool::buildPath(TEMP_PATH, $iconZipPath);
            $uploadResult = self::uploadToFileManageSystem($iconPath, 'appIcon', 'XHSVADAPBUABML');
            //确认图标所在文件夹并删除
            $iconZipPath = explode('/', $iconZipPath)[0];
            Tool::delDirAndFile(Tool::buildPath(TEMP_PATH, $iconZipPath));
            if (!$uploadResult) {
                @unlink($fullPath);
                throw new \Exception('图标上传失败！');
            }
            $appInfo['icon'] = $uploadResult;
        }
        return $appInfo;
    }

    /**
     * 解析IOS应用信息并返回
     *
     * @param $fullPath
     * @param $appInfo
     * @return array
     * @throws \Exception
     *
     * create by ck 20170609
     * modify by ck 20181123
     */
    private static function parseIOSInfo($fullPath, $appInfo)
    {
        $parseConfig = self::getConfigByName('appParse');
//        $tempPath    = $parseConfig['tempPath'];

        //提取解析plist文件，上传图标，打开失败则直接返回
        $zip = new \ZipArchive;
        if($zip->open($fullPath) !== true) {
            @unlink($fullPath);
            throw new \Exception($fullPath . '解压失败！');
        }

        //处理文件在压缩包内路径并解压
        $zipPath   = $zip->getNameIndex(1);
        $plistPath = $zipPath. 'Info.plist';
        $iconPath  = $zipPath. 'AppIcon60x60@3x.png';
        $zip->extractTo(TEMP_PATH, $plistPath);                                             //解压plist文件
        $zip->extractTo(TEMP_PATH, $iconPath);                                              //解压图标
        $iconPath  = Tool::buildPath(TEMP_PATH, $iconPath);
        $zip->close();

        //解析plist文件内容并处理数组
        $plist    = file_get_contents(Tool::buildPath(TEMP_PATH, $plistPath));
        $property = new CFPropertyList();
        $property->parse($plist);
        $plist = $property->toArray();
        $appInfo['packageName'] = $plist['CFBundleIdentifier'];
        $appInfo['name']        = $plist['CFBundleName'];
        $appInfo['versionCode'] = $plist['CFBundleVersion'];
        $appInfo['versionName'] = $plist['CFBundleShortVersionString'];

        //上传图标
        $uploadResult = self::uploadToFileManageSystem($iconPath, 'appIcon', 'XHSVADAPBUABML');
        Tool::delDirAndFile(Tool::buildPath(TEMP_PATH, explode('/', $zipPath)[0]));
        if(!$uploadResult) {
            @unlink($fullPath);
            throw new \Exception('图标上传失败！');
        }
        $appInfo['icon'] = $uploadResult;
        $fileName  = basename($fullPath);
        $plistPath = Tool::buildPath(TEMP_PATH, $fileName . '.plist');
        $plist     = fopen($plistPath, 'w');                                                //生成plist文件
        $content   = <<<PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>items</key>
    <array>
        <dict>
            <key>assets</key>
            <array>
                <dict>
                    <key>kind</key>
                    <string>software-package</string>
                    <key>url</key>
                    <string>{$appInfo['url']}</string>
                </dict>
            </array>
            <key>metadata</key>
            <dict>
                <key>bundle-identifier</key>
                <string>{$appInfo['packageName']}</string>
                <key>bundle-version</key>
                <string>{$appInfo['versionCode']}</string>
                <key>kind</key>
                <string>software</string>
                <key>title</key>
                <string>{$appInfo['name']}</string>
            </dict>
        </dict>
    </array>
</dict>
</plist>
PLIST;
        fwrite($plist, $content);
        fclose($plist);
        $result = self::uploadToFileManageSystem($plistPath, 'plist', 'XHSVADAPBUABML', true);
        @unlink($plistPath);
        if(!$result) {
            @unlink($fullPath);
            throw new \Exception('plist文件上传失败！');
        }
        $appInfo['plistUrl'] = $result;
        return $appInfo;
    }

    /**
     * 根据权限创建表单按钮数组
     *
     * @param $powerTag
     * @param $ridBtn
     * @return array $formButtonArr
     *
     * create by ck 20170622
     * modify by ck 20170627    添加排除数组
     * modify by ck 20170722    tag可传入bool值
     */
    public static function createFormButton($powerTag, $ridBtn = [])
    {
        $formButtonArr = [];
        is_string($ridBtn) and $ridBtn = [$ridBtn];
        if($powerTag !== false) {
            if (!in_array('add', $ridBtn) && ($powerTag === true || UserPower::checkUserPower($powerTag . '_Add'))) {
                $formButtonArr[] = ['id' => 'bt_form_add', 'text' => '保存', 'class' => 'disabled'];
            }
            if (!in_array('edit', $ridBtn) && ($powerTag === true || UserPower::checkUserPower($powerTag . '_Edit'))) {
                $formButtonArr[] = ['id' => 'bt_form_edit', 'text' => '保存', 'class' => 'disabled'];
            }
        }
        !in_array('reset', $ridBtn) and $formButtonArr[] = ['id' => 'bt_form_reset', 'text' => '重置'];
        !in_array('back', $ridBtn)  and $formButtonArr[] = ['id' => 'bt_form_back', 'text' => '取消'];
        return $formButtonArr;
    }

    /**
     * 根据学校ID连接学校主库
     *
     * @param $schoolId
     * @param $dbName
     * @param bool $useMain
     * @return bool|Mysql
     *
     * create by ck 20170628
     */
    public static function getSchoolMainDB($schoolId, &$dbName, $useMain = true)
    {
        static $dbArr = [], $dbNameArr = [], $oldCenterDb = null;
        if(!$schoolId) return false;
        isset($dbNameArr[$schoolId]) and $dbName = $dbNameArr[$schoolId];
        if(isset($dbArr[$schoolId])){
            return $dbArr[$schoolId];
        }
        if(APP_DEV){
            $dbName = 'xh_zjhzlcyh_school';
            return new Mysql([
                'host'      => '192.168.5.53',
                'username'  => 'webmail_user',
                'password'  => 'szw_webmail1234',
                'dbname'    => 'xh_zjhzlcyh_school',
                'charset'   => 'latin1'
            ]);
        }
        if(!$oldCenterDb){
            $config = Main::getConfigByName('oldCenterDB');
            $oldCenterDb = new Mysql($config);
        }
        $school = $oldCenterDb->fetchOne("select sy_id,sy_name,sy_name3 from system_type where sy_id= '{$schoolId}'");
        if(!$school) return false;
        $schoolInfo = explode('|', $school['sy_name3']);
        if(count($schoolInfo) < 6) return false;
        $dbName = explode('/', $schoolInfo[1]);
        $dbName = end($dbName);
        $dbIp   = end($schoolInfo);
        if($dbIp == '172.16.10.20' && !$useMain) {
            $dbIp = '172.16.10.21';
        }
        elseif($dbIp == '172.16.10.22' && !$useMain){
            $dbIp = '172.16.10.23';
        }
        $db = new Mysql([
            'host'      => $dbIp,
//            'username'  => 'readonly',
//            'username'  => Di::getDefault()->get('config')->get('database')['username'],
            'username'  => 'webmail_user',
//            'password'  => 'lVf641AO',
//            'password'  => Di::getDefault()->get('config')->get('database')['password'],
            'password'  => 'szw_webmail1234',
            'dbname'    => $dbName,
            'charset'   => 'latin1'
        ]);
        $dbArr[$schoolId]     = $db;
        $dbNameArr[$schoolId] = $dbName;
        return $db;
    }

    /**
     * 发送验证码
     *
     * @param $phoneNum
     * @param null $code
     * @param string $app
     * @param string $instance
     * @param string $channelId
     * @param $codeType
     * @return bool
     * @throws \Exception
     *
     * create by ck 20180208
     */
    public static function sendValidCode($phoneNum, $code = null, $app = 'admin', $instance = '', $channelId = '', $codeType = 0)
    {
        if (!preg_match('/^1[3456789]{1}\d{9}$/', $phoneNum)) {
            throw new \Exception('手机号格式不正确');
        }
        if (!$code) {
            $code = Tool::getRand(6);
        }
        $validCode = new XhovertValidcode();
        $validCode->sSelfPhone  = $phoneNum;
        $validCode->iVCode      = $code;
        $validCode->tCreateTime = date('Y-m-d H:i:s');
        $validCode->tExpireTime = date('Y-m-d H:i:s', strtotime($validCode->tCreateTime) + 60 * 10);
        $validCode->iCodeType   = $codeType;
        $validCode->iState      = 0;
        if (!$validCode->save()) {
            throw new \Exception('验证码生成失败');
        }
        self::sendSMS($phoneNum, "您的验证码：{$code}，十分钟内有效。", $app, $instance, $channelId);
//        $url  = self::getConfigByName('message')['url'];
//        $data = [
//            'appId'	    => 'platform',
//            'clientId'	=> $phoneNum,
//            'payload'	=> "您的验证码：{$code}，十分钟内有效。",
//            'type'		=> 'SMS'
//        ];
//        $http = new HttpClient($url);
//        $response = $http->postJson('api/v1/messages', $data);
//        if (!$response->isSuccess()) {
//            throw new \Exception('验证码发送失败:' . $response->body);
//        }
//        $body = $response->toObject();
//        if ($body->code != 'OK') {
//            throw new \Exception('验证码发送失败:' . $response->body);
//        }
        return true;
    }

    /**
     * 发送短信
     *
     * @param $phone
     * @param $msg
     * @param $app
     * @param $instance
     * @param $channelId
     * @return bool
     * @throws \Exception
     *
     * create by ck 20180412
     * create by qgz 20180826 $channelId默认值
     */
    public static function sendSMS($phone, $msg, $app = '', $instance = '', $channelId = 'SMS_SHIYUAN')
    {
    	if (!$channelId){
		    $channelId = 'SMS_SHIYUAN';
	    }
        $url  = self::getConfigByName('message')['url'];
        $data = [
            'appId'	            => 'platform',
            'clientId'	        => $phone,
            'payload'	        => $msg,
            'type'		        => 'SMS',
            'sourceAppId'       => $app,
            'sourceInstanceId'  => $instance,
            'channelId'         => $channelId
        ];
        $http = new HttpClient($url);
        $response = $http->postJson('api/v1/messages', $data);
        $body = $response->toObject();
        if (!$response->isSuccess()) {
            throw new \Exception($body->msg ?: '发送失败');
        }
        if ($body->code != 'OK') {
            throw new \Exception($body->msg);
        }
        return true;
    }

    /**
     * 检查验证码是否有效
     *
     * @param $phoneNum
     * @param $validCode
     * @param int $codeType
     * @return bool
     *
     * create by ck 20180208
     * modify by ck 20180524    仅最后一条有效
     */
    public static function checkValidCode($phoneNum, $validCode, $codeType = 0)
    {
//        $checkCode = XhovertValidcode::findFirst([
//            'conditions' => 'sSelfPhone = :phone: AND iVCode = :code: AND tExpireTime >= :nowTime: AND (iCodeType = 0 OR iCodeType = :codeType:)',
//            'bind'       => ['phone' => $phoneNum, 'code' => $validCode, 'nowTime' => date('Y-m-d H:i:s'), 'codeType' => $codeType],
//        ]);
        $checkCode = XhovertValidcode::findFirst([
            'conditions' => 'sSelfPhone = :phone: AND tExpireTime >= :nowTime: AND (iCodeType = 0 OR iCodeType = :codeType:)',
            'bind'       => ['phone' => $phoneNum, 'nowTime' => date('Y-m-d H:i:s'), 'codeType' => $codeType],
            'order'      => 'iVCId DESC'
        ]);
        if (!$checkCode || $checkCode->iVCode != $validCode) {
            return false;
        }
        $checkCode->iState = 1;
        $checkCode->save();
        return true;
    }

    /**
     * 根据id添加年级信息
     *
     * @param $obj 数据array
     * @param array $fields key=>value,key为所根据的用户id，value为添加年级的字段名
     * @param bool $enumName 是否将年级code替换成枚举
     *
     * @return bool
     *
     * create by qgz 20180912
     */
    public static function addUserGradeById(&$obj, $fields=[], $enumName=false)
    {
        $enumArr = [11 => '小学一年级', 12 => '小学二年级', 13 => '小学三年级', 14 => '小学四年级', 15 => '小学五年级',
                    16=> '小学六年级', 21 => '初一', 22 => '初二', 23 => '初三', 31 => '高一', 32 => '高二', 33 => '高三'];
        $idArr = [];
        if (!$fields) return true;
        foreach($fields as $idField => $nameField){
            $idArr = array_merge($idArr, array_column($obj, $idField));
        }
        if (!$idArr) return true;
        $userInfo = XhsysUser::find([
            'columns'    => 'iUserId,iGradeCode',
            'conditions' => 'iUserId IN ({idArr:array})',
            'bind'       => ['idArr' => array_values(array_unique($idArr))]
        ])->toArray();
        if(!$userInfo) return true;
        $userGradeInfo = array_column($userInfo, 'iGradeCode', 'iUserId');
        foreach($obj as $index => $objOne){
            foreach($fields as $idField => $gradeField){
                if(!isset($userGradeInfo[$objOne[$idField]])) continue;
                $obj[$index][$gradeField] =  $enumName && isset($enumArr[$userGradeInfo[$objOne[$idField]]]) ?
                    $enumArr[$userGradeInfo[$objOne[$idField]]] : $userGradeInfo[$objOne[$idField]];
            }
        }
        return true;
    }

    /**
     * 根据用户id添加班级信息
     *
     * @param $obj 数据array
     * @param array $fields key=>value,key为所根据的用户id，value为添加年级的字段名
     *
     * @return bool
     *
     * create by qgz 20180912
     */
    public static function addUserClassById(&$obj, $fields=[])
    {
        $idArr = [];
        if (!$fields) return true;
        foreach($fields as $idField => $nameField){
            $idArr = array_merge($idArr, array_column($obj, $idField));
        }
        if (!$idArr) return true;
        $userInfo = XhsysUser::find([
            'columns'    => 'iUserId,sAdClsName',
            'conditions' => 'iUserId IN ({idArr:array})',
            'bind'       => ['idArr' => array_values(array_unique($idArr))]
        ])->toArray();
        if(!$userInfo) return true;
        $userClassInfo = array_column($userInfo, 'sAdClsName', 'iUserId');
        foreach($obj as $index => $objOne){
            foreach($fields as $idField => $classField){
                if(!isset($userClassInfo[$objOne[$idField]])) continue;
                $obj[$index][$classField] =  $userClassInfo[$objOne[$idField]];
            }
        }
        return true;
    }



}